const SERVICE_NAME = "NotificationUtils";

class NotificationUtils {
	static SERVICE_NAME = SERVICE_NAME;

	constructor({ stringService, emailService, settingsService }) {
		this.stringService = stringService;
		this.emailService = emailService;
		this.settingsService = settingsService;
	}

	get serviceName() {
		return NotificationUtils.SERVICE_NAME;
	}

	buildTestEmail = async () => {
		const context = { testName: "Monitoring System" };
		const html = await this.emailService.buildEmail("testEmailTemplate", context);
		return html;
	};

	buildStatusEmail = async (networkResponse) => {
		const { monitor, status, prevStatus } = networkResponse;
		const template = prevStatus === false ? "serverIsUpTemplate" : "serverIsDownTemplate";
		const context = { monitor: monitor.name, url: monitor.url };
		const subject = `Monitor ${monitor.name} is ${status === true ? "up" : "down"}`;
		const html = await this.emailService.buildEmail(template, context);
		return { subject, html };
	};

	buildWebhookMessage = (networkResponse) => {
		const { monitor, status, code, timestamp } = networkResponse;
		// Format timestamp using the local system timezone
		const formatTime = (timestamp) => {
			const date = new Date(timestamp);

			// Get timezone abbreviation and format the date
			const timeZoneAbbr = date.toLocaleTimeString("en-US", { timeZoneName: "short" }).split(" ").pop();

			// Format the date with readable format
			return (
				date
					.toLocaleString("en-US", {
						year: "numeric",
						month: "2-digit",
						day: "2-digit",
						hour: "2-digit",
						minute: "2-digit",
						second: "2-digit",
						hour12: false,
					})
					.replace(/(\d+)\/(\d+)\/(\d+),\s/, "$3-$1-$2 ") +
				" " +
				timeZoneAbbr
			);
		};

		// Get formatted time
		const formattedTime = timestamp ? formatTime(timestamp) : formatTime(new Date().getTime());

		// Create different messages based on status with extra spacing
		let messageText;
		if (status === true) {
			messageText = this.stringService.monitorUpAlert
				.replace("{monitorName}", monitor.name)
				.replace("{time}", formattedTime)
				.replace("{code}", code || "Unknown");
		} else {
			messageText = this.stringService.monitorDownAlert
				.replace("{monitorName}", monitor.name)
				.replace("{time}", formattedTime)
				.replace("{code}", code || "Unknown");
		}
		const dn = this.stringService.discordNotification;
		let discordMessageText = {
			embeds: [
				{
					title: status
						? dn.uptimeAlert.replace("{monitorName}", monitor?.name ?? dn.unknown)
						: dn.downtimeAlert.replace("{monitorName}", monitor?.name ?? dn.unknown),
					color: status ? 5763719 : 15548997,

					fields: [
						{ name: dn.monitor, value: monitor?.name ?? dn.unknown, inline: true },
						{ name: dn.status, value: status ? dn.up : dn.down, inline: true },
						{ name: dn.statusCode, value: String(code ?? dn.unknown), inline: true },
						{ name: dn.time, value: formattedTime, inline: true },
						{ name: dn.url, value: monitor?.url ?? dn.unknown, inline: false },
					],
					footer: { text: dn.checkmate },
				},
			],
		};
		return [messageText, discordMessageText];
	};

	buildHardwareAlerts = async (networkResponse) => {
		const monitor = networkResponse?.monitor;
		const thresholds = networkResponse?.monitor?.thresholds;
		const { usage_cpu: cpuThreshold = -1, usage_memory: memoryThreshold = -1, usage_disk: diskThreshold = -1 } = thresholds;

		const metrics = networkResponse?.payload?.data;
		const { cpu: { usage_percent: cpuUsage = -1 } = {}, memory: { usage_percent: memoryUsage = -1 } = {}, disk = [] } = metrics;

		const { clientHost } = this.settingsService.getSettings();

		const alerts = {
			cpu: cpuThreshold !== -1 && cpuUsage > cpuThreshold ? true : false,
			memory: memoryThreshold !== -1 && memoryUsage > memoryThreshold ? true : false,
			disk: disk?.some((d) => diskThreshold !== -1 && typeof d?.usage_percent === "number" && d?.usage_percent > diskThreshold) ?? false,
		};

		const alertsToSend = [];
		const discordEmbeds = [];
		const monitorInfoFields = [
			{ name: "Monitor", value: monitor.name, inline: true },
			{ name: "URL", value: monitor.url, inline: false },
		];
		const goToIncidentField = { name: `Go to incident`, value: `${clientHost}/infrastructure/${monitor._id}` };
		const formatDiscordAlert = {
			cpu: () => ({
				title: "CPU alert",
				description: `Your current CPU usage (${(cpuUsage * 100).toFixed(0)}%) is above your threshold (${(cpuThreshold * 100).toFixed(0)}%)`,
				color: 15548997,
				fields: [...monitorInfoFields, goToIncidentField],
				footer: { text: "Checkmate" },
			}),

			memory: () => ({
				title: "Memory alert",
				description: `Your current memory usage (${(memoryUsage * 100).toFixed(0)}%) is above your threshold (${(memoryThreshold * 100).toFixed(0)}%)`,
				color: 15548997,
				fields: [...monitorInfoFields, goToIncidentField],
				footer: { text: "Checkmate" },
			}),

			disk: () => ({
				title: "Disk alert",
				description: `Your current disk usage is above your threshold (${(diskThreshold * 100).toFixed(0)}%)`,
				color: 15548997,
				footer: { text: "Checkmate" },
				fields: [
					...monitorInfoFields,
					...(Array.isArray(disk) ? disk : []).map((d, idx) => ({
						name: `Disk ${idx}`,
						value: `${(d?.usage_percent * 100).toFixed(0)}%`,
						inline: true,
					})),
					goToIncidentField,
				],
			}),
		};
		const alertTypes = ["cpu", "memory", "disk"];
		for (const type of alertTypes) {
			// Iterate over each alert type to see if any need to be decremented
			if (alerts[type] === true) {
				monitor[`${type}AlertThreshold`]--; // Decrement threshold if an alert is triggered

				if (monitor[`${type}AlertThreshold`] <= 0) {
					// If threshold drops below 0, reset and send notification
					monitor[`${type}AlertThreshold`] = monitor.alertThreshold;

					const formatAlert = {
						cpu: () => `Your current CPU usage (${(cpuUsage * 100).toFixed(0)}%) is above your threshold (${(cpuThreshold * 100).toFixed(0)}%)`,
						memory: () =>
							`Your current memory usage (${(memoryUsage * 100).toFixed(0)}%) is above your threshold (${(memoryThreshold * 100).toFixed(0)}%)`,
						disk: () =>
							`Your current disk usage: ${disk
								.map((d, idx) => `(Disk${idx}: ${(d.usage_percent * 100).toFixed(0)}%)`)
								.join(", ")} is above your threshold (${(diskThreshold * 100).toFixed(0)}%)`,
					};
					alertsToSend.push(formatAlert[type]());
					discordEmbeds.push(formatDiscordAlert[type]());
				}
			}
		}
		await monitor.save();
		const discordPayload = discordEmbeds.length ? { embeds: discordEmbeds } : null;
		return [alertsToSend, discordPayload];
	};

	buildHardwareEmail = async (networkResponse, alerts) => {
		const { monitor } = networkResponse;
		const template = "hardwareIncidentTemplate";
		const context = { monitor: monitor.name, url: monitor.url, alerts };
		const subject = `Monitor ${monitor.name} infrastructure alerts`;
		const html = await this.emailService.buildEmail(template, context);
		return { subject, html };
	};

	buildHardwareNotificationMessage = (alerts, monitor) => {
		const { clientHost } = this.settingsService.getSettings();
		const alertsHeader = [`Monitor: ${monitor.name}`, `URL: ${monitor.url}`];
		const alertFooter = [`Go to incident: ${clientHost}/infrastructure/${monitor._id}`];
		const alertText = alerts.length > 0 ? [...alertsHeader, ...alerts, ...alertFooter] : [];
		return alertText.map((alert) => alert).join("\n");
	};
	buildHardwareWebhookBody = (alerts, monitor) => {
		const content = alerts.map((alert) => alert).join("\n");
		const body = { text: content, name: monitor.name, url: monitor.url };
		return body;
	};
}

export default NotificationUtils;
